'''
Created on Aug 23, 2010

@author: adolfo

Unit test suite for the classes in charge of handling the reception of packets.
'''
import logging
import unittest
import array
from springpython.context import ApplicationContext
from mx.org.came.tests.core_test_application_context import CoreTestConfig
from mx.org.came.core import SMSAppStoreDispatcher, _get_session_id, \
    _get_total_packets_in_session, SessionHandler, _Session
import time
import random

_LOGGER = logging.getLogger("mx.org.came.tests.core_test")

# Pylint reports that we access protected members as an error (in these tests
# we do that on purpose.
#pylint: disable=W0212
class SMSAppStoreDispatcherTest(unittest.TestCase):
    """Class that defines tests for the methods in class
    ``core.SMSAppStoreDispatcher``"""

    # Spring application context with the objects to be used in the
    # unit test suite.
    CONTEXT = ApplicationContext(CoreTestConfig())

    def setUp(self):
        """Does nothing."""
        pass
    
    def tearDown(self):
        """Does nothing"""
        pass

    def test_get_session_id(self):
        """Verifies that the function ``_get_session_id(packet)`` retrieves the
        session's ID from the packet.
        """
        session = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "single_packet_session")
        self.assertEquals(_get_session_id(session.received_packets[0])
                          , 0
                          , "Unexpected session ID!")

    def test_get_total_packets_in_session(self):
        """Verifies that the function ``_get_total_packets_in_session(packet)``
        retrieves the number of packets that make up the session a packet
        belongs to.
        """
        session = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "single_packet_session")
        self.assertEquals(_get_total_packets_in_session(
                                  session.received_packets[0])
                          , 1
                          , "Unexpected total number of packets!")

    def test_get_packet_index(self):
        """Verifies that the function ``_get_packet_index(packet)``
        retrieves the index of the packet in the sequence that makes up
        the session.
        """
        session = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "single_packet_session")
        self.assertEquals(_get_total_packets_in_session(
                                  session.received_packets[0])
                          , 1
                          , "Unexpected packet index!")


    def test_udh(self):
        """Test that the UDH used by SMSAppStore is the one expected."""
        self.assertEquals(SMSAppStoreDispatcher.SMSAPPSTORE_UDH
                          , "\x06\x05\x04\xdf\x8b\x00\x00"
                          , "The header constant was not the one expected.")
    
    def  test_start_service(self):
        """Verifies that the ``SMSAppStoreDispatcher`` service can be started,
        and stops on request.
        """
        smsappstore_dispatcher = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "smsappstore_dispatcher")
        smsappstore_dispatcher.start()
        self.assertTrue(smsappstore_dispatcher.isAlive()
                        , "SMSAppStoreDispatcher did not start on request!")
        smsappstore_dispatcher.shutdown()
        time.sleep(1) # wait for the thread to die
        self.assertFalse(smsappstore_dispatcher.isAlive()
                         , "SMSAppStoreDispatcher did not stop on request!")
        
    
    def test_get_session_handler(self):
        """Verifies that ``_get_session_handler()`` creates properly named
        threads that timeout correctly.
        """
        smsappstore_dispatcher = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "smsappstore_dispatcher")
        thread1 = smsappstore_dispatcher._get_session_handler("0123456789")
        self.assertEquals(thread1.name
                          , "0123456789"
                          , "Thread named incorrectly!")
        thread2 = smsappstore_dispatcher._get_session_handler("0123456789")
        thread3 = smsappstore_dispatcher._get_session_handler("9876543210")
        self.assertEquals(thread1
                          , thread2
                          , "Different sessions for the same number!")
        self.assertNotEqual(thread1
                            , thread3
                            , "Same session for different numbers!")
        time.sleep(smsappstore_dispatcher._session_handler_timeout - 1)
        self.assertTrue(thread1.isAlive()
                        , "Session timed-out before expected")
        time.sleep(2) # wait for the thread to die
        self.assertFalse(thread1.isAlive()
                         , "Session alive after expected")

class SessionHandlerTest(unittest.TestCase):
    """Class that defines tests for the methods in the module
    ``kannel_sms_gateway``"""

    def setUp(self):
        """Does nothing."""
        pass
    
    def tearDown(self):
        """Does nothing"""
        pass
    
    def test_add_packet_to_session(self):
        """Verify that packets are being gathered correctly."""
        # Single-packet session
        handler = SessionHandler(None, None, 3)
        packet = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "single_packet_session").received_packets[0]
        handler.add_packet_to_session(packet)
        session = handler._get_session(_get_session_id(packet))
        self.assertTrue(len(session.received_packets) == 1
                        , "The packet was not stored!")
        self.assertEquals(session.received_packets[0]
                          , packet
                          , "Different packets!")
        self.assertTrue(session.is_complete()
                          , "Expects more packets than those in the session!")
        del(session)
        # Multiple-packet session
        packets = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "multiple_packet_session").received_packets
        order = range(len(packets))
        random.shuffle(order) # add the packets to the session in a random order
        handler.add_packet_to_session(packets[order.pop(0)])
        session = handler._get_session(_get_session_id(packets[0]))
        self.assertFalse(session.is_complete()
                         , "Session is not complete, yet it reports it is!")
        for i in order:
            handler.add_packet_to_session(packets[i])
        self.assertTrue(len(session.received_packets) == len(packets)
                        , "Some packets were not stored!")
        self.assertEquals(session.received_packets
                          , packets
                          , "Packets were not stored in the right order!")
        self.assertTrue(session.is_complete()
                          , "Expects more packets than those in the session!")
        self.assertTrue(handler._contains_session(_get_session_id(packet))
                        and
                        handler._contains_session(_get_session_id(packets[0]))
                          , "Both sessions were gathered by the same handler!")

    def test_get_encoded_queries_from_packet(self):
        """Verify that the queries inside packets are retrieved correctly."""
        # Single-query packet
        handler = SessionHandler(None, None, 3)
        packet = array.array("B"
                             , "\x01\x00\x01\x00\x01\x01\x04\x00\x00\x63\xa8")
        expected_queries = [array.array("B", [0, 0, 0x63, 0xa8])]
        queries = handler._get_encoded_queries_from_packet(packet)
        self.assertEquals(queries
                          , expected_queries
                          , "The encoded query was not retrieved correctly!")
        # Multiple-query packet
        packet = array.array("B", "\x01\x0b\x01\x00\x0b\x02"
                                  "\x04\x00\x00\x63\xa8"
                                  "\x04\x00\x00\x63\xa8")
        expected_queries = [array.array("B", [0, 0, 0x63, 0xa8])
                            , array.array("B", [0, 0, 0x63, 0xa8])]
        queries = handler._get_encoded_queries_from_packet(packet)
        self.assertEquals(queries
                          , expected_queries
                          , "The encoded queries were not retrieved correctly!")

    def test_decode_query(self):
        """Verify that encoded queries are decoded correctly."""
        smsappstore_dispatcher = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "smsappstore_dispatcher")
        handler = SessionHandler(None, smsappstore_dispatcher, 3)
        packet = SMSAppStoreDispatcherTest.CONTEXT.get_object(
                "single_packet_session").received_packets[0]
        encoded_query = handler._get_encoded_queries_from_packet(packet)[0]
        expected_decoded_query = {
                'decoded_query': {'schemaName': 'Client', 'Group ID': '=25512'
                                  , 'dbName': 'CAME', 'logical': 'AND'}
                , 'app_dict' : smsappstore_dispatcher.application_reader.\
                        parseFile(None)
                , 'form_dict' : smsappstore_dispatcher.form_reader.\
                        parseFile(None, None)}
        decoded_query = handler._decode_query(encoded_query)
        self.assertEquals(decoded_query
                          , expected_decoded_query
                          , "The encoded query was not decoded correctly!")
